
# error, exception
# 语法错误, 即解析错.
# 异常, 运行期间检测到的错误. 大多数的异常都不会被程序处理,都以错误信息的形式展现.

# 异常处理-try-except-else-finally
'''
如果在执行try子句的过程中发生了异常,那么try子句余下的部分将被忽略.
如果异常的类型和 except 之后的名称相符,那么对应的except子句将被执行.
如果一个异常没有与任何的except匹配,那么这个异常将会传递给上层的try中.
一个 try 语句可能包含多个except子句,分别来处理不同的特定的异常.最多只有一个分支会被执行.
一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组.
最后一个except子句可以忽略异常的名称,被当作通配符使用.可以用来打印一个错误信息,并且用raise把异常抛出.
异常处理并不仅仅处理那些直接发生在try子句中的异常,而且还能处理子句中调用的函数（甚至间接调用的函数）里抛出的异常.
'''
while True:
    try:
        int(input("Press ENTER to continue..."))
    except KeyboardInterrupt:
        print("\n<Ctrl-C><KeyboardInterrupt>")
        break
    except EOFError:
        print("\n<Ctrl-Z><EOFError>")
        break
    except ValueError as vError:
        print("<ValueError><%s>" % vError)
print()

# 用户自定义异常
# 创建一个新的 Exception 类来自定义异常,异常应直接或间接继承自 Exception 类.
class CustomError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)
try:
    raise CustomError('This is my custom error!')
except CustomError as err:
    print('='*8, 'CustomError', '='*8)
    print("CustomError:", err.value)
print()

# 当创建一个模块有可能抛出多种不同的异常时,
# 一种通常的做法是为这个包建立一个基础异常类,
# 然后基于这个基础类为不同的错误情况创建不同的子类.
class Error(Exception):
    """Base class for exceptions in this module."""
    pass
class InputError(Error):
    """Exception raised for errors in the input.
    Attributes:
        expression -- input expression in which the error occurred
        message -- explanation of the error
    """
    def __init__(self, expression, message):
        self.expression = expression
        self.message = message
class TransitionError(Error):
    """Raised when an operation attempts a state transition that's not
    allowed.
    Attributes:
        previous -- state at beginning of transition
        next -- attempted new state
        message -- explanation of why the specific transition is not allowed
    """
    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

# 不管 try 子句里面有没有发生异常,finally 子句都会执行.
# 如果一个异常在 try 子句里（或者在 except 和 else 子句里）被抛出,
# 而又没有任何的 except 把它截住,那么这个异常会在 finally 子句执行后再次被抛出.
def divide(x, y):
    try:
        result = x / y
    except ZeroDivisionError:
        print("<ZeroDivisionError>Division by zero!")
    else:
        print("<else>Result is", result)
    finally:
        print("<finally>Executing finally clause!")
try:
    divide(2, 1)
    divide(2, 0)
    divide('2', '1')
except:
    print("<Other exception>raise after finally")
print()

# 预定义的清理行为
# 关键词 with 可以保证诸如文件之类的对象在使用完之后一定会正确的执行它的清理方法.
with open("python.os") as fd:
    for line in fd:
        print(line, end='')
try:
    print(fd.readlines())
except ValueError as verr:
    print('<ValueError>', verr)
print()

# 使用 raise 语句抛出一个指定的异常
print('='*8, 'raise', '='*8) # 执行
raise NameError('Specify exception!')
print('='*8, 'raise', '='*8) # 不会执行
